#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>

using namespace std;

const int gbasesize = 1024;
const int gargvnum = 64;
const int genvnum = 64;
const char *gsep = " ";

//全局命令行参数列表
//ls -a -l -n
char *gargv[gargvnum];
int gargc = 0;

//全局的重定向参数
//ls -a -l -n >> file.txt
//ls -a -l -n > file.txt
//ls -a -l -n < file.txt
#define NoneRedir   0
#define InPutRedir  1
#define OutPutRedir 2
#define AppendRedir 3
int Redir = NoneRedir;
char * filename = nullptr;
#define TrimSpace(pos) do {\
    while(isspace(*pos)) {\
        pos++;\
    }\
}while(0)

//退出码
enum ExitCode
{
    SubprocessExit = 1,
    InPutRedirExit,
    FilenameErr,
    OutPutRedirExit,
    AppendRedirExit
};

//返回信息
enum LastnodeCode
{
    Sucess = 0,
    CdErr,
    ExportErr,
    EchoErr
};


//全局的上一个指令的执行的返回信息
int lastnode = Sucess;

//我们自己的环境变量表
//PWD,NAME,HOSTNEME...
char *genv[genvnum];

//全局的当前的工作路径
//PWD,CWD
char pwd[gbasesize];
//全局的环境变量中的工作路径
char pwdenv[gbasesize];


string GetUserName()
{
    const char* name = getenv("USER");
    return name ? name : "None";
}

string GetHostName()
{
    //getnev("HOSTNAME");
    //硬编码
    return "hcss-ecs-0190";
}

string GetPwd()
{
    if(getcwd(pwd,gbasesize) == nullptr) return "None";
    //更新环境变量中的工作路径
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
    // char* pwd = getenv("PWD");
    // return pwd ? pwd : "None";
}

string Lastdir()
{
    string curr = GetPwd();
    if(curr == "/" || curr == "None") return curr;
    size_t pos = curr.rfind("/");
    if(pos == std::string::npos) return curr;
    return curr.substr(pos + 1);
}

string MakeCommandLine()
{
    char command_line[gbasesize];
    snprintf(command_line, gbasesize, "[%s@%s %s]# ",
            GetUserName().c_str(), GetHostName().c_str(), Lastdir().c_str());
    return command_line;
}

void PrintCommandLine()
{
    printf("%s", MakeCommandLine().c_str());
    fflush(stdout);
}

bool GetCommandLine(char command_buffer[],int len)
{
    char *results = fgets(command_buffer,len,stdin);
    if(!results) return false;
    command_buffer[strlen(command_buffer) - 1] = 0;
    if(strlen(command_buffer) == 0) return false; //没有命令
    return true;
}

void InitlCommandLine()
{
    //先清空缓冲区
    memset(gargv,0,sizeof gargv);
    gargc = 0;

    //初始化重定向
    Redir = NoneRedir;
    filename = nullptr;
}

void ParseRedirCommandLine(char command_buffer[],int len)
{
    //分析重定向内容
    int end = len - 1;
    while(end >= 0)
    {
        if(command_buffer[end] == '<')
        {
            Redir = InPutRedir;
            command_buffer[end] = 0;
            filename = &command_buffer[end] + 1;
            TrimSpace(filename);
            break;
        }
        else if(command_buffer[end] == '>')
        {
            if(command_buffer[end-1] == '>')
            {
                Redir = AppendRedir;
                command_buffer[end] = 0;
                command_buffer[end-1] = 0;
                filename = &command_buffer[end] + 1;
                TrimSpace(filename);
                break;
            }
            else
            {
                Redir = OutPutRedir;
                command_buffer[end] = 0;
                filename = &command_buffer[end] + 1;
                TrimSpace(filename);
                break;
            }
        }
        else
        {
            end--;
        }
    }
}

void ParseCommand(char command_buffer[])
{
    gargv[gargc++] = strtok(command_buffer,gsep);
    while((bool)(gargv[gargc++] = strtok(nullptr,gsep)));
    gargc--;
}

void ParseCommandLine(char command_buffer[],int len)
{
    
    InitlCommandLine();
    ParseRedirCommandLine(command_buffer,len);
    ParseCommand(command_buffer);

    // printf("###################\n");
    // printf("%s\n",command_buffer);
    
    // printf("%s\n",command_buffer);
    // printf("%d\n",Redir);
    // // printf("%s\n",filename);
}

void debug()
{
    printf("%d\n",gargc);
    for(int i = 0;gargv[i];i++)
    {
        printf("gargv[%d]:%s\n",i,gargv[i]);
    }
}

//重定向，子进程执行不影响父进程
void DoRedir()
{
    if(Redir == InPutRedir)
        {
            if(filename)
            {
                int fd = open(filename,O_RDONLY);
                if(fd < 0) exit(InPutRedirExit);
                dup2(fd,0);
            }
            else exit(FilenameErr);
        }
    else if(Redir == OutPutRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
            if(fd < 0) exit(OutPutRedirExit);
            dup2(fd,1);
        }
        else exit(FilenameErr);
    }
    else if(Redir == AppendRedir)
    {
        if(filename)
        {
            int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);
            if(fd < 0) exit(AppendRedirExit);
            dup2(fd,1);
        }
        else exit(FilenameErr);
    }
    else 
    {
        //Do nothing
    }
}
//shell中内建命令不能让子进程执行
bool ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0) return false;
    if(id == 0)
    {
        //处理重定向
        DoRedir();
        //子进程执行命令并退出
        execvpe(gargv[0],gargv,genv); //替换为自己的环境变量表
        exit(SubprocessExit);
    }
    int status = 0;
    pid_t rid = waitpid(id,&status,0);
    if(rid > 0) 
    {
        if(WIFEXITED(status))
        {
            lastnode = WEXITSTATUS(status);
        }
        else lastnode = 113;
        return true;
    }

    return false;
}

//添加环境变量
void AddEnv(const char *item)
{
    int index = 0;
    while(genv[index++]);
    genv[--index] = (char*)malloc(strlen(item) + 1);
    strncpy(genv[index],item,strlen(item) + 1);
    genv[++index] = nullptr;
}

//检查并执行内建命令
bool CheckAndExecuBuildCommand()
{
    if(strcmp(gargv[0],"cd") == 0) //切换工作路径
    {
        if(gargc == 2)
        {
            chdir(gargv[1]); //更改工作路径
            lastnode = Sucess;
        }
        else lastnode = CdErr;
        return true;
    }
    else if(strcmp(gargv[0],"export") == 0) //添加环境变量
    {
        if(gargc == 2)
        {
            AddEnv(gargv[1]);
            lastnode = Sucess;
        }
        else lastnode = ExportErr;
        return true;
    }
    else if(strcmp(gargv[0],"env") == 0) //打印自己的环境变量表
    {
        if(gargc == 1)
        {
            for(int i = 0;genv[i];i++)
            {
                printf("%s\n",genv[i]);
            }
            lastnode = Sucess;
        }
        return true;
    }
    else if(strcmp(gargv[0],"echo") == 0)
    {
        if(gargc == 2)
        {
            if(gargv[1][0] == '$')
            {
                if(gargv[1][1] == '?')
                {
                    printf("%d\n",lastnode);
                    lastnode = Sucess;
                }
                else printf("%s\n",gargv[1]),lastnode = Sucess;

            }
            else printf("%s\n",gargv[1]),lastnode = Sucess;
        }
        else lastnode = EchoErr;
        return true;
    }
    return false;
}

//在shell中，启动时环境变量会从系统的文件中被解析出来
//我们今天直接拷贝父进程的作为自己的环境变量表
void InitEnv()
{
    extern char **environ;
    int index = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index]) + 1); //包含\0的大小
        strncpy(genv[index],environ[index],strlen(environ[index]) + 1);
        index++;
    }
    genv[index] = nullptr;
}

int main()
{
    InitEnv();

    char command_buffer[gbasesize];
    while(true)
    {

        PrintCommandLine(); //1.打印命令行提示符
        // printf("\n");
        // sleep(1);

        InitEnv();

        if(!GetCommandLine(command_buffer,gbasesize))   //2.获取用户命令（含输出型参数，返回获取的命令
        {
            continue;
        }
        // printf("%s\n",command_buffer);

        ParseCommandLine(command_buffer,strlen(command_buffer));

        //debug();

        //检测内建命令
        if(CheckAndExecuBuildCommand())
        {
            continue;
        }

        ExecuteCommand();   //4.执行分析好的命令
    }

    return 0;
}